package com.milo.stream;


import com.milo.stream.entity.Artist;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;

import java.util.*;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.stream.Collectors.partitioningBy;
import static java.util.stream.Collectors.toList;
import static org.junit.Assert.assertEquals;

/**
 * @author: Milogenius
 * @create: 2019-04-19 15:09
 * @description:Stream学习
 **/
@Slf4j
public class StreamTest {

    private StreamTest[] ssss = new StreamTest[555];

    /**
     * 生成流
     * 在 Java 8 中, 集合接口有两个方法来生成流：
     * stream() − 为集合创建串行流。
     * parallelStream() − 为集合创建并行流。
     */
    @Test
    public void stream() {

        List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jkl");
        List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(toList());
        System.out.println(filtered.toString());
    }

    /**
     * 生成其他集合,可以使用{@link Collectors#toCollection(Supplier)}
     */
    @Test
    public void toCollection() {

        List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jkl");
        /*TreeSet<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toCollection(TreeSet::new));
        System.out.println(filtered.toString());*/
        Map<Integer, String> collect = strings.stream().map(a -> {
            return "milo";
        }).collect(Collectors.toMap(s -> s.length(), a -> a, (o, n) -> o));

    }

    /**
     * forEach
     * Stream 提供了新的方法 'forEach' 来迭代流中的每个数据。以下代码片段使用 forEach 输出了10个随机数：
     */
    @Test
    public void forEach1() {
        Random random = new Random();
        random.ints().limit(5).forEach(System.out::println);
    }

    /**
     * 只能遍历一次
     */
    @Test
    public void forEach2() {
        List<String> title = Arrays.asList("Wmyskxz", "Is", "Learning", "Java8", "In", "Action");
        Stream<String> s = title.stream();
        s.forEach(System.out::println);
        s.forEach(System.out::println);
    }

    /**
     * map
     * map 方法用于映射每个元素到对应的结果，以下代码片段使用 map 输出了元素对应的平方数：
     */
    @Test
    public void map() {
        List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
        // 获取对应的平方数
        List<Integer> squaresList = numbers.stream().map(i -> i * i).distinct().collect(toList());
        System.out.println(squaresList.toString());

        Stream.of("apple","banana","orange","waltermaleon","grape")
                .map(String::length) //转成单词的长度 int
                .forEach(System.out::println);
    }


    @Test
    public void mapToLong(){
        Stream.of("apple", "banana", "orange", "waltermaleon", "grape")
                .mapToLong(e -> e.length()) //转成long ,本质上是int 但是存在类型自动转换
                .forEach(System.out::println);
    }

    @Test
    public void mapToDouble(){
        Stream.of("apple", "banana", "orange", "waltermaleon", "grape")
                .mapToDouble(e -> e.length()) //转成Double ，自动类型转换成Double
                .forEach(System.out::println);
    }

    static class User {

        private String name;

        private int age;

        public User(String name, int age) {
            this.name = name;
            this.age = age;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        @Override
        public String toString() {
            return "User{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }

    /**
     * peek挑选 ，将元素挑选出来，可以理解为提前消费
     */
    @Test
    public void peek(){
        User w = new User("w",10);
        User x = new User("x",11);
        User y = new User("y",12);

        Stream.of(w,x,y)
                .peek(e->{e.setName(e.getAge()+e.getName());}) //重新设置名字 变成 年龄+名字
                .forEach(e->System.out.println(e.toString()));
    }


    /**
     * filter
     * filter 方法用于通过设置的条件过滤出元素。以下代码片段使用 filter 方法过滤出空字符串：
     */
    @Test
    public void filter() {
        List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jkl");
        // 获取空字符串的数量
        long count = strings.stream().filter(string -> string.isEmpty()).count();
        System.out.println(count);
    }

    /**
     * limit
     * limit 方法用于获取指定数量的流。 以下代码片段使用 limit 方法打印出 10 条数据：
     */
    @Test
    public void limit() {
        Random random = new Random();
        random.ints().limit(10).forEach(System.out::println);
    }

    /**
     * sorted
     * sorted 方法用于对流进行排序。以下代码片段使用 sorted 方法对输出的 10 个随机数进行排序：
     */
    @Test
    public void sorted() {
        log.info("例子1===============");
        Random random = new Random();
        random.ints().limit(10).sorted().forEach(System.out::println);

        log.info("例子2==========");
        Stream.of(2,1,3,6,4,9,6,8,0)
                .sorted()
                .forEach(e->System.out.println(e));
    }

    /**
     * 并行（parallel）程序
     * parallelStream 是流并行处理程序的代替方法。以下实例我们使用 parallelStream 来输出空字符串的数量：
     */
    @Test
    public void parallelStream() {
        List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jkl");
        // 获取空字符串的数量
        long count = strings.parallelStream().filter(string -> string.isEmpty()).count();
        System.out.println(count);
    }

    /**
     * Collectors
     */
    @Test
    public void collect() {
        List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jkl");
        List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(toList());

        System.out.println("筛选列表: " + filtered);
        String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
        System.out.println("合并字符串: " + mergedString);
    }

    /**
     * 统计
     * <p>
     *     使用mapToInt将Integer转换为基本类型int操作,提高性能
     *     {@link IntSummaryStatistics#IntSummaryStatistics()} 可以计算出最小值/最大值/平均值/数值总和
     * </p>
     */
    @Test
    public void mapToInt() {
        log.info("例子1==========");
        Stream.of("apple", "banana", "orange", "waltermaleon", "grape")
                .mapToInt(e -> e.length()) //转成int
                .forEach(System.out::println);

        log.info("例子2==========");
        List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);

        IntSummaryStatistics stats = numbers.stream().mapToInt((x) ->x).summaryStatistics();

        System.out.println("列表中最大的数 : " + stats.getMax());
        System.out.println("列表中最小的数 : " + stats.getMin());
        System.out.println("所有数之和 : " + stats.getSum());
        System.out.println("平均数 : " + stats.getAverage());
        //断言
        assertEquals(7,stats.getMax());
    }



    /**
     * distinct
     * 流还支持一个叫做 distinct 的方法，它会返回一个元素各异（根据流所生成的元素的 hashCode 和 equals 方法实现）的流
     */
    @Test
    public void distinct() {
        List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 2, 1, 3, 4);
        numbers.stream()
                .filter(integer -> integer % 2 == 0)
                .distinct()
                .forEach(System.out::println);
    }

    /**
     * skip
     * 流还支持 skip(n) 方法，返回一个扔掉了前 n 个元素的流。如果流中元素不足 n 个，则返回一个空流。
     */
    @Test
    public void skip() {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
        numbers.stream()
                .filter(integer -> integer % 2 == 0)
                .skip(2)
                .forEach(System.out::println);
    }

    /**
     * anyMatch
     * 集合里面是否有偶数
     */
    @Test
    public void anyMatch() {
        List<Integer> numbers = Arrays.asList(1, 2, 3);
        if (numbers.stream().anyMatch(integer -> integer % 2 == 0)) {
            System.out.println("集合里有偶数!");
        }

    }

    /**
     * allMatch
     * 集合里都为偶数
     */
    public void allMatch() {
        List<Integer> numbers = Arrays.asList(2, 2, 4);
        if (numbers.stream().allMatch(integer -> integer % 2 == 0)) {
            System.out.println("集合里全是偶数!");
        }
    }

    /**
     * findFirst
     * 给定一个数字列表，找出第一个平方能被 3 整除的数
     */
    @Test
    public void findFirst() {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
        Optional<Integer> first = numbers.stream()
                .map(x -> x * x)
                .filter(x -> x % 3 == 0)
                .findFirst();
        System.out.println(first.get());
    }

    /**
     * findAny:获取流中任意一个元素
     */
    @Test
    public void findAny(){
        Optional<String> stringOptional = Stream.of("apple", "banana", "orange", "waltermaleon", "grape")
                .parallel()
                .findAny(); //在并行流下每次返回的结果可能一样也可能不一样
        stringOptional.ifPresent(e->System.out.println(e));
    }

    /**
     *判断数据流中 一个都没有与aa 相等元素 ，但是流中存在 aa ，所以最终结果应该是false
     */
    @Test
    public void noneMatch () {
        boolean result = Stream.of("aa","bb","cc","aa")
                .noneMatch(e->e.equals("aa"));
        System.out.println(result);

    }

    /**
     *min:元素中最小的
     */
    @Test
    public void min() {
        Optional<Integer> integerOptional = Stream.of(0,9,8,4,5,6,-1)
                .min((e1,e2)->e1.compareTo(e2));
        integerOptional.ifPresent(e->System.out.println(e));
    }

    /**
     *max:元素中最大的
     */
    @Test
    public void max() {
        Optional<Integer> integerOptional = Stream.of(0,9,8,4,5,6,-1)
                .max((e1,e2)->e1.compareTo(e2));
        integerOptional.ifPresent(e->System.out.println(e));
    }

    /**
     *reduce:是一个规约操作，所有的元素归约成一个，比如对所有元素求和，乘啊等
     */
    @Test
    public void reduce(){
        int sum = Stream.of(0,9,8,4,5,6,-1)
                .reduce(0,(e1,e2)->e1+e2);
        System.out.println(sum);
    }
    /**
     * flatMap : 将多个Stream连接成一个Stream
     */
    @Test
    public void flatMap(){
        log.info("例子1==========");
        ArrayList<Integer> list1 = new ArrayList<>();
        list1.add(1);
        list1.add(2);
        ArrayList<Integer> list2 = new ArrayList<>();
        list2.add(3);
        list2.add(4);
        List<Integer> together =   Stream.of(list1,list2).flatMap(number -> number.stream()).collect(toList());
        System.out.println("together:"+together.toString());

        log.info("例子2==========");
        Stream.of("a-b-c-d","e-f-i-g-h")
                .flatMap(e->Stream.of(e.split("-")))
                .forEach(e->System.out.println(e));
    }

     /**
     * 数据分块
     * 将艺术家组成的流分成乐队和独唱歌手两部分
     * {@link Collectors#partitioningBy} 接受一个流,将其分成两部分
     * @param artist
     * @return
     */
    @Test
    public Map<Boolean,List<Artist>> bandsAndSolo(Stream<Artist> artist){
        //return artist.collect(partitioningBy(artist -> artist.isSolo()));
        return artist.collect(partitioningBy(Artist::isSolo));
    }


}
